package com.xiguaji.tiktok.task.aweme.event;

import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.gugee.tiktok.common.model.dal.blogger.AwemeRepository;
import com.gugee.tiktok.common.model.mysql.Aweme;
import com.gugee.tiktok.common.model.spider.aweme.Aweme_list;
import com.xiguaji.boot.eventbus.Event;
import com.xiguaji.boot.eventbus.EventBusTemplate;
import com.xiguaji.boot.eventbus.IEventListener;
import com.xiguaji.boot.filecenter.FileTemplate;
import com.xiguaji.boot.toolkit.DateTimeUtils;
import com.xiguaji.boot.toolkit.ThreadPoolUtils;
import com.xiguaji.tiktok.task.aweme.client.BloggerClient;
import com.xiguaji.tiktok.task.core.ScheduleTemplate;
import com.xxl.job.core.log.XxlJobLogger;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 爬虫保存推荐流数据
 */
@Slf4j
@Component
public class SaveAwemeEventListener implements IEventListener<SaveAwemeEventListener.SaveAwemeEvent> {

    @Autowired
    AwemeRepository awemeRepository;

    @Autowired
    Aweme.AwemeMapper awemeMapper;

    @Autowired
    BloggerClient bloggerClient;

    @Autowired
    FileTemplate fileTemplate;

    @Autowired
    ScheduleTemplate scheduleTemplate;

    @Value("${xiguaji.oss.bucket.tiktok.videoImg}")
    String picBucket;

    @Autowired
    @Lazy
    EventBusTemplate eventBusTemplate;
    ForkJoinPool forkJoinPoolDownloadPics = ThreadPoolUtils.forkJoinPool(200, "downloadPics");
    @Autowired
    RedisTemplate<String,String> redisTemplate;
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

    @PostConstruct
    public void init() {
        log.info("SaveAwemeEventListener  init");
        scheduleTemplate.attach("downloadPics", forkJoinPoolDownloadPics);
    }

    @Override
    @Subscribe
    @AllowConcurrentEvents
    public void handle(SaveAwemeEvent event) {
       try{ process(event);}
       catch (Exception e){
           log.error(event.uid+":"+e.getMessage());
           XxlJobLogger.log(e);
       }
    }

    public void process(SaveAwemeEvent event) {
//        final long[] s3CostTotal = {0};
        long one=System.currentTimeMillis();
        if (CollectionUtils.isEmpty(event.aweme_lists)) {
            return;
        }
        long start = System.currentTimeMillis();

        var awemeEntities = awemeMapper.mapper(event.aweme_lists);
        // List<Aweme> fetchAwemeCommentIds = new ArrayList<>();
        event.saveAwemeTimeStatistics.setMapperTimes((System.currentTimeMillis()-start)/1000);
        List<Aweme> insert = new ArrayList<>();
        List<Aweme> update = new ArrayList<>();

        String bloggerType = bloggerClient.getBloggerType(event.uid);
        event.saveAwemeTimeStatistics.setQueryBloggerTypeTimes((System.currentTimeMillis()-start)/1000);
        long two=System.currentTimeMillis();

        awemeEntities.forEach(aweme -> {
            long fm=System.currentTimeMillis();
            LocalDateTime localDateTime = DateTimeUtils.localDateTimeBySecond(aweme.getOCreateTime());
            aweme.setDatecode(Integer.parseInt(localDateTime.format(formatter)));
            event.saveAwemeTimeStatistics.setFormatDateTimes(event.saveAwemeTimeStatistics.getFormatDateTimes()+(System.currentTimeMillis()-fm));

            if(!StringUtils.isEmpty(bloggerType)){
                aweme.setBloggerType(bloggerType);
            }

            long startAweme = System.currentTimeMillis();
            val awemeOptional = awemeRepository.findByoAwemeId(aweme.getOAwemeId());
            event.saveAwemeTimeStatistics.setQueryAwemeTimes(event.saveAwemeTimeStatistics.getQueryAwemeTimes()+(System.currentTimeMillis()-startAweme));
            if((System.currentTimeMillis()-startAweme)/1000 >5)
                log.info("SaveAwemeEventListener startAweme use time:"+(System.currentTimeMillis()-startAweme)/1000+"s");

            if(awemeOptional.isPresent()){
                aweme.setId(awemeOptional.get().getId());
                aweme.setScore(awemeOptional.get().getScore());
                if(awemeOptional.get().getOCover().contains("oss-us-east-1.aliyuncs.com")){
                    aweme.setOCover(awemeOptional.get().getOCover());
                }else if(awemeOptional.get().getOCover().contains("s3.amazonaws.com")){
                    aweme.setOCover(awemeOptional.get().getOCover().replace("s3.amazonaws.com","oss-us-east-1.aliyuncs.com"));
                }
            }

            if(aweme.getId() > 0){
                long updateTs = System.currentTimeMillis();
                update.add(aweme);
                event.saveAwemeTimeStatistics.setUpdateAweme2ListTimes(event.saveAwemeTimeStatistics.getUpdateAweme2ListTimes()+(System.currentTimeMillis()-updateTs));
            }else{
                long addTs = System.currentTimeMillis();
                insert.add(aweme);
                event.saveAwemeTimeStatistics.setAddAweme2ListTimes(event.saveAwemeTimeStatistics.getAddAweme2ListTimes()+(System.currentTimeMillis()-addTs));
            }
        });

        if(update.size() > 0){
            long updateStart = System.currentTimeMillis();
            awemeRepository.batchUpdate(update);
            event.saveAwemeTimeStatistics.setBatchUpdateTimes((System.currentTimeMillis()-updateStart)/1000);
            if((System.currentTimeMillis()-updateStart)/1000 >1)
                log.info("SaveAwemeEventListener batchUpdate count:"+update.size()+" use time:"+(System.currentTimeMillis()-updateStart)/1000+"s");
        }
        long downPic=System.currentTimeMillis();
        //新视频，多线程下载图片
        if(insert.size()==1){
            Aweme aweme =insert.get(0);
            aweme.setOCover(setS3Picture(aweme.getOAwemeId(),aweme.getOCover(),event.saveAwemeTimeStatistics));
        }else {
            try {
                forkJoinPoolDownloadPics.submit(
                        () -> {
                            insert.parallelStream().forEach(aweme -> {

                                        aweme.setOCover(setS3Picture(aweme.getOAwemeId(), aweme.getOCover(), event.saveAwemeTimeStatistics));

                                    }
                            );
                        }
                ).get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        event.saveAwemeTimeStatistics.downPic2S3Times.addAndGet((System.currentTimeMillis()-downPic)/1000);
        long third=System.currentTimeMillis();
        if (insert.size() > 0){
            long saveStart = System.currentTimeMillis();
            awemeRepository.batchSave(insert);
            event.saveAwemeTimeStatistics.setBatchInsertTimes((System.currentTimeMillis()-saveStart)/1000);
            if((System.currentTimeMillis()-saveStart)/1000 >1)
                log.info("SaveAwemeEventListener batchSave count:"+insert.size()+" use time:"+(System.currentTimeMillis()-saveStart)/1000+"s");
        }

//        log.info("=========== one={},two={},third={}",(two- one) / 1000,(third - two) / 1000,(System.currentTimeMillis() - third) / 1000);
        if((System.currentTimeMillis()-start)/1000 >5) {
            event.saveAwemeTimeStatistics.setAllTimes((System.currentTimeMillis() - start) / 1000);
            event.saveAwemeTimeStatistics.setInsertSize(insert.size());
            event.saveAwemeTimeStatistics.setUpdateSize(update.size());
            log.info("---------SaveAwemeEventListener update counts={},timesCount:{}", awemeEntities.size(), event.saveAwemeTimeStatistics);
        }
   }

    private boolean isRefreshAwemeComment(Optional<Aweme> origin, Aweme target, RefreshAwemeCommentType type) {
        if (type == RefreshAwemeCommentType.EXPLICIT) return true;
        if (type == RefreshAwemeCommentType.IGNORE) return false;

        //如果第一次采集，评论数大于20 则采集
        if (origin.isEmpty() && target.getOCommentCount() > 20) return true;
        //如果更新数大于20且原始采集数少于1000则采集
        if (origin.isPresent() && origin.get().getOCommentCount() < 1000 &&
                (target.getOCommentCount() - origin.get().getOCommentCount() > 20)) return true;

        return false;
    }

    private String setS3Picture(long awemeId,String coverUrl,SaveAwemeTimeStatistics saveAwemeTimeStatistics){
        String s3Url= coverUrl;
        try {

            redisTemplate.opsForValue().set("aweme_"+awemeId,coverUrl);
            s3Url = fileTemplate.putNetImg(picBucket,"video_" + DateTimeUtils.millsecond2DateStr(System.currentTimeMillis() / 1000,"yyyyMMdd") + "/" + awemeId,coverUrl);

        } catch (Exception ioException) {
            //上传失败则当不曾上传过
            log.error("**************************************************"+ioException.getMessage());
        }
        redisTemplate.delete("aweme_"+awemeId);
        if(StringUtils.isEmpty(s3Url)){
            s3Url= coverUrl;
            log.error(String.format("setOssPicture:awemeId=%s;coverUrl=%s;ossUrl=%s;", awemeId, coverUrl,s3Url));
        }

        return s3Url;
    }
    @Getter
    public static class SaveAwemeEvent extends Event {
        public SaveAwemeEvent(Event event, List<Aweme_list> aweme_lists, RefreshAwemeCommentType refreshAwemeCommentType) {
            super(event, "SaveAweme");
            this.aweme_lists = aweme_lists;
            this.refreshAwemeCommentType = refreshAwemeCommentType;
        }

        public SaveAwemeEvent(List<Aweme_list> aweme_lists, RefreshAwemeCommentType refreshAwemeCommentType) {
            this((Event) null, aweme_lists, refreshAwemeCommentType);
        }
        public SaveAwemeEvent(Long uid, List<Aweme_list> aweme_lists, RefreshAwemeCommentType refreshAwemeCommentType) {
            super(null, "SaveAweme");
            this.aweme_lists = aweme_lists;
            this.uid=uid;
            this.refreshAwemeCommentType = refreshAwemeCommentType;
        }
        private List<Aweme_list> aweme_lists;
        private Long uid;
        private RefreshAwemeCommentType refreshAwemeCommentType;
        private SaveAwemeTimeStatistics saveAwemeTimeStatistics=new SaveAwemeTimeStatistics();

    }

    public enum RefreshAwemeCommentType {
        NORMAL,
        IGNORE,
        EXPLICIT
    }
    @Getter
    @Setter

    public static class SaveAwemeTimeStatistics{
        private long allTimes=0;
        private long batchInsertTimes=0;
        private long batchUpdateTimes=0;
        private AtomicLong downPic2S3Times=new AtomicLong();
        private long redis4Pic2S3Times=0;
        private long queryAwemeTimes=0;

        private long queryBloggerTypeTimes=0;
        private long addAweme2ListTimes=0;
        private long updateAweme2ListTimes=0;
        private long formatDateTimes=0;
        private long mapperTimes=0;
        private int insertSize=0;
        private int updateSize=0;

        @Override
        public String toString() {
            return "SaveAwemeTimeStatistics{" +
                    "allTimes=" + allTimes +
                    ", insertSize=" + insertSize +
                    ", batchInsertTimes=" + batchInsertTimes +
                    ", updateSize=" + updateSize +
                    ", batchUpdateTimes=" + batchUpdateTimes +
                    ", downPic2S3Times=" + downPic2S3Times +
                    ", redis4Pic2S3Times=" + redis4Pic2S3Times/1000 +
                    ", queryAwemeTimes=" + queryAwemeTimes/1000 +
                    ", queryBloggerTypeTimes=" + queryBloggerTypeTimes +
                    ", addAweme2ListTimes=" + addAweme2ListTimes/1000 +
                    ", updateAweme2ListTimes=" + updateAweme2ListTimes/1000 +
                    ", formatDateTimes=" + formatDateTimes/1000 +
                    ", mapperTimes=" + mapperTimes +

                    '}';
        }
    }
}